#pragma once
#include <iostream>
#include <string>
#include <memory>
#include <unordered_map>
#include "Connection.hpp"
#include "Epoll.hpp"
#include "Common.hpp"

class Reactor {
private:
    const static int gtimeout = 1000;
    static const int gnum = 128;

    bool ConnectionIsExist(int fd) {
        return _sock_connection.find(fd) != _sock_connection.end();
    }

    void LoopOnce(int n) {

        struct epoll_event* ev = _epoller->ReadyEvents(); 
        for (int i = 0; i < n; i++) {
            int fd = ev[i].data.fd;
            if(!ConnectionIsExist(fd))
                continue;
            // 现在就可以通过fd找到对应的connection链接
            // 让对应的连接去做对应的事情
            if (ev[i].events & EPOLLERR) 
                ev[i].events |= (EPOLLIN | EPOLLOUT);
            
            if (ev[i].events & EPOLLHUP)
                ev[i].events |= (EPOLLIN | EPOLLOUT);
            
            if (ev[i].events & EPOLLIN)
                _sock_connection[fd]->_handler_recver(_sock_connection[fd]);
            
            if (ev[i].events & EPOLLOUT)
                _sock_connection[fd]->_handler_sender(_sock_connection[fd]);
        }
    }

    void PrintDebug() {
        std::string fdlist;
        for (auto& it : _sock_connection)
            fdlist += std::to_string(it.first) + " ";
        LOG(INFO, "epoll's fd list: %s\n", fdlist.c_str());
    }
public:
    Reactor(uint16_t port)
    {
        _epoller = std::make_unique<Epoller>();
        _isrunning = false;
    }

    void AddConnection(int sockfd, uint32_t ep_op, InetAdrr addr, int type) {
        Connection* conn = new Connection(sockfd, this, addr);

        // 还需要加入到epoll的底层中
        struct epoll_event event;
        event.data.fd = sockfd;
        ep_op |= EPOLLET;
        event.events = ep_op;

        conn->SetEvents(ep_op);

        _epoller->AddEvents(sockfd, &event);

        if (type == ListenConnection)
            conn->AddHanlder(_on_listen, nullptr, nullptr);
        else
            conn->AddHanlder(_on_recver, _on_sender, _on_excepter);

        _sock_connection.insert(std::make_pair(sockfd, conn));
        LOG(INFO, "add a connection, sockfd: %d\n", sockfd);
    }

    void SetListenConnection(handler_t listen_conn) {
        _on_listen = listen_conn;
    }

    void SetNormalConnection(handler_t recver, handler_t sender, handler_t excepter) {
        _on_excepter = excepter;
        _on_recver = recver;
        _on_sender = sender;
    }

    void Dispatcher() {
        int timeout = gtimeout;
        _isrunning = true;
        while (true) {
            // std::cout << "xxxxxxxxxxxx" << std::endl;
            int n = _epoller->Wait(timeout);
            // std::cout << "-----------" << std::endl;
            LoopOnce(n);
            PrintDebug();
        }
        _isrunning = false;
    }

    void EnableConnectionReadWrite(int fd, bool read, bool write) {
        if (!ConnectionIsExist(fd)) return;
        uint32_t epoll_flag = EPOLLET | (read ? EPOLLIN : 0) | (write ? EPOLLOUT : 0);
        struct epoll_event ev;
        ev.data.fd = fd;
        ev.events = epoll_flag;
        _epoller->ModEvents(fd, &ev);
    }

    void CloseReactorSocket(Connection* conn_self) {
        // 从epoll中移除
        // 从map中删除，然后将空间释放
        // 关闭sock
        int fd = conn_self->Sockfd();
        if (!ConnectionIsExist(fd)) return;
        Connection* conn = _sock_connection[fd];

        LOG(DEBUG, "close the socked: %d , and realse the source, client info: %s\n", fd, conn->_addr.AddrString().c_str());

        struct epoll_event ev;
        ev.events = conn->GetEvents();
        ev.data.fd = fd;

        EnableConnectionReadWrite(fd, false, false);
        _epoller->DelEvents(fd, &ev);


        delete _sock_connection[fd];
        _sock_connection.erase(fd);
        close(fd);
    }

    ~Reactor() {}
private:
    // 需要将对应的套接字接口和链接加入到如下
    std::unordered_map<int, Connection*> _sock_connection;
    // Epoller _epoller;
    std::unique_ptr<Multiplex> _epoller;
    // struct epoll_event _events[gnum];
    bool _isrunning;

    handler_t _on_listen;
    handler_t _on_recver;
    handler_t _on_sender;
    handler_t _on_excepter;
};

